home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / mail.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  16KB  |  700 lines

  1. /*  mail - send/receive mail          Author: Peter S. Housel */
  2.  
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <errno.h>
  6. #undef EOF            /* temporary hack */
  7. #include <signal.h>
  8. #include <pwd.h>
  9. #include <time.h>
  10. #include <setjmp.h>
  11. #include <string.h>
  12. #include <stdio.h>
  13.  
  14. #ifdef DEBUG
  15. #define D(Q) (Q)
  16. #else
  17. #define D(Q)
  18. #endif
  19.  
  20. #define SHELL        "/bin/sh"
  21.  
  22. #define DROPNAME     "/usr/spool/mail/%s"
  23. #define LOCKNAME    "/usr/spool/mail/%s.lock"
  24. #define LOCKWAIT    5    /* seconds to wait after collision */
  25. #define LOCKTRIES    4    /* maximum number of collisions */
  26.  
  27. #define MBOX        "mbox"
  28.  
  29. #define HELPFILE    "/usr/lib/mail.help"
  30. #define PROMPT        "? "
  31. #define PATHLEN        80
  32. #define MAXRCPT        100    /* maximum number of recipients */
  33. #define LINELEN        512
  34.  
  35. /* #define MAILER        "/usr/bin/smail"    /* smart mailer */
  36. #define MAILERARGS        /* (unused) */
  37.  
  38. #define UNREAD        1    /* 'not read yet' status */
  39. #define DELETED        2    /* 'deleted' status */
  40. #define READ        3    /* 'has been read' status */
  41.  
  42. extern int optind;        /* getopt() globals */
  43. extern char *optarg;
  44. extern int errno;
  45. extern char *malloc();
  46.  
  47. struct letter {
  48.   struct letter *prev, *next;    /* linked letter list */
  49.   int status;            /* letter status */
  50.   off_t location;        /* location within mailbox file */
  51. };
  52.  
  53. struct letter *firstlet, *lastlet;
  54.  
  55. int usemailer = 1;        /* use MAILER to deliver (if any) */
  56. int printmode = 0;        /* print-and-exit mode */
  57. int quitmode = 0;        /* take interrupts */
  58. int reversemode = 0;        /* print mailbox in reverse order */
  59. int usedrop = 1;        /* read the maildrop (no -f given) */
  60. int verbose = 0;        /* pass "-v" flag on to mailer */
  61. int needupdate = 0;        /* need to update mailbox */
  62. char mailbox[PATHLEN];        /* user's mailbox/maildrop */
  63. char tempname[PATHLEN] = "/tmp/mailXXXXXX";    /* temporary file */
  64. FILE *boxfp = NULL;        /* mailbox file */
  65. jmp_buf printjump;        /* for quitting out of letters */
  66. unsigned oldmask;        /* saved umask() */
  67.  
  68. int deliver();
  69. FILE *makerewindable();
  70. int copy();
  71. void readbox(), printall(), interact(), savelet(), updatebox();
  72. void printlet(), doshell(), usage();
  73. void onint();
  74. char *basename(), *whoami();
  75. extern FILE *fopen(), *freopen(), *fdopen();
  76. extern struct passwd *getpwnam(), *getpwuid();
  77. extern char *getenv();
  78.  
  79. main(argc, argv)
  80. int argc;
  81. char *argv[];
  82. {
  83.   int c;
  84.  
  85.   if ('l' == (basename(argv[0]))[0])    /* 'lmail' link? */
  86.     usemailer = 0;        /* yes, let's deliver it */
  87.  
  88.   (void) mktemp(tempname);    /* name the temp file */
  89.  
  90.   oldmask = umask(022);        /* change umask for security */
  91.  
  92.   while (EOF != (c = getopt(argc, argv, "epqrf:t:dv"))) switch (c) {
  93.         case 'e':
  94.         case 't':
  95.         fprintf(stderr, "option not implemented yet\n");
  96.         /* Because I don't know what they do */
  97.         exit(1);
  98.  
  99.         case 'p':    ++printmode;    break;
  100.  
  101.         case 'q':    ++quitmode;    break;
  102.  
  103.         case 'r':    ++reversemode;    break;
  104.  
  105.         case 'f':
  106.         setuid(getuid());    /* won't need to lock */
  107.         usedrop = 0;
  108.         strncpy(mailbox, optarg, PATHLEN - 1);
  109.         break;
  110.  
  111.         case 'd':    usemailer = 0;    break;
  112.  
  113.         case 'v':    ++verbose;    break;
  114.  
  115.         default:
  116.         usage();
  117.         exit(1);
  118.     }
  119.  
  120.   if (optind < argc) {
  121.     if (deliver(argc - optind, argv + optind) < 0)
  122.         exit(1);
  123.     else
  124.         exit(0);
  125.   }
  126.   if (usedrop) sprintf(mailbox, DROPNAME, whoami());
  127.  
  128.   D(printf("mailbox=%s\n", mailbox));
  129.  
  130.   readbox();
  131.  
  132.   if (printmode)
  133.     printall();
  134.   else
  135.     interact();
  136.  
  137.   if (needupdate) updatebox();
  138.  
  139.   exit(0);
  140. }
  141.  
  142. int deliver(count, vec)
  143. int count;
  144. char *vec[];
  145. {
  146.   int i;
  147.   int errs = 0;            /* count of errors */
  148.   int dropfd;            /* file descriptor for user's drop */
  149.   int created = 0;        /* true if we created the maildrop */
  150.   FILE *mailfp;            /* fp for mail */
  151.   struct stat stb;        /* for checking drop modes, owners */
  152.   void (*sigint) (), (*sighup) (), (*sigquit) ();    /* saving signal state */
  153.   time_t now;            /* for datestamping the postmark */
  154.   char sender[32];        /* sender's login name */
  155.   char lockname[PATHLEN];    /* maildrop lock */
  156.   int locktries;        /* tries when box is locked */
  157.   struct passwd *pw;        /* sender and recipent */
  158.  
  159.   if (count > MAXRCPT) {
  160.     fprintf(stderr, "mail: too many recipients\n");
  161.     return -1;
  162.   }
  163. #ifdef MAILER
  164.   if (usemailer) {
  165.     char *argvec[MAXRCPT + 3];
  166.     char **argp;
  167.  
  168.     setuid(getuid());
  169.  
  170.     argp = argvec;
  171.     *argp++ = "send-mail";
  172.     if (verbose) *argp++ = "-v";
  173.  
  174.     for (i = 0; i < count; ++i) *argp++ = vec[i];
  175.  
  176.     *argp = (char *) NULL;
  177.     execv(MAILER, argvec);
  178.     fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
  179.     return -1;
  180.   }
  181. #endif /* MAILER */
  182.  
  183.   if (NULL == (pw = getpwuid(getuid()))) {
  184.     fprintf(stderr, "mail: unknown sender\n");
  185.     return -1;
  186.   }
  187.   strcpy(sender, pw->pw_name);
  188.  
  189.   /* If we need to rewind stdin and it isn't rewindable, make a copy */
  190.   if (isatty(0) || (count > 1 && lseek(0, 0L, 0) < 0L)) {
  191.     mailfp = makerewindable();
  192.   } else
  193.     mailfp = stdin;
  194.  
  195.   /* Shut off signals during the delivery */
  196.   sigint = signal(SIGINT, SIG_IGN);
  197.   sighup = signal(SIGHUP, SIG_IGN);
  198.   sigquit = signal(SIGQUIT, SIG_IGN);
  199.  
  200.   for (i = 0; i < count; ++i) {
  201.     if (count > 1) rewind(mailfp);
  202.  
  203.     D(printf("deliver to %s\n", vec[i]));
  204.  
  205.     if (NULL == (pw = getpwnam(vec[i]))) {
  206.         fprintf(stderr, "mail: user %s not known\n", vec[i]);
  207.         ++errs;
  208.         continue;
  209.     }
  210.     sprintf(mailbox, DROPNAME, pw->pw_name);
  211.     sprintf(lockname, LOCKNAME, pw->pw_name);
  212.  
  213.     D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname));
  214.  
  215.     /* Lock the maildrop while we're messing with it. Races are
  216.      * possible (though not very likely) when we have to create
  217.      * the maildrop, but not otherwise. If the box is already
  218.      * locked, wait awhile and try again. */
  219.     locktries = created = 0;
  220. trylock:
  221.     if (link(mailbox, lockname) != 0) {
  222.         if (ENOENT == errno) {    /* user doesn't have a drop yet */
  223.             if ((dropfd = creat(mailbox, 0600)) < 0) {
  224.                 fprintf(stderr, "mail: couln't create a maildrop for user %s\n",
  225.                     vec[i]);
  226.                 ++errs;
  227.                 continue;
  228.             }
  229.             ++created;
  230.             goto trylock;
  231.         } else {    /* somebody else has it locked, it seems -
  232.              * wait */
  233.             if (++locktries >= LOCKTRIES) {
  234.                 fprintf(stderr, "mail: couldn't lock maildrop for user %s\n",
  235.                     vec[i]);
  236.                 ++errs;
  237.                 continue;
  238.             }
  239.             sleep(LOCKWAIT);
  240.             goto trylock;
  241.         }
  242.     }
  243.     if (created) {
  244.         (void) chown(mailbox, pw->pw_uid, pw->pw_gid);
  245.         boxfp = fdopen(dropfd, "a");
  246.     } else
  247.         boxfp = fopen(mailbox, "a");
  248.  
  249.     if (NULL == boxfp || stat(mailbox, &stb) < 0) {
  250.         fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]);
  251.         unlink(lockname);
  252.         ++errs;
  253.         continue;
  254.     }
  255.     if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
  256.         fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]);
  257.         unlink(lockname);
  258.         ++errs;
  259.         continue;
  260.     }
  261.     (void) time(&now);
  262.     fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
  263.  
  264.     if ((copy(mailfp, boxfp) < 0) | (fclose(boxfp) != 0)) {
  265.         fprintf(stderr, "mail: error delivering to user %s", vec[i]);
  266.         perror("");
  267.         ++errs;
  268.     }
  269.     unlink(lockname);
  270.   }
  271.  
  272.   fclose(mailfp);
  273.  
  274.   /* Put signals back the way they were */
  275.   signal(SIGINT, sigint);
  276.   signal(SIGHUP, sighup);
  277.   signal(SIGQUIT, sigquit);
  278.  
  279.   return(0 == errs) ? 0 : -1;
  280. }
  281.  
  282. /* 'stdin' isn't rewindable. Make a temp file that is.
  283.  * Note that if one wanted to catch SIGINT and write a '~/dead.letter'
  284.  * for interactive mails, this might be the place to do it (though the
  285.  * case where a MAILER is being used would also need to be handled).
  286.  */
  287. FILE *makerewindable()
  288. {
  289.   FILE *tempfp;            /* temp file used for copy */
  290.   int c;            /* character being copied */
  291.   int state;            /* ".\n" detection state */
  292.  
  293.   if (NULL == (tempfp = fopen(tempname, "w"))) {
  294.     fprintf(stderr, "mail: can't create temporary file");
  295.     return NULL;
  296.   }
  297.  
  298.   /* Here we copy until we reach the end of the letter (end of file or
  299.    * a line containing only a '.'), painstakingly avoiding setting a
  300.    * line length limit. */
  301.   state = '\n';
  302.   while (EOF != (c = getc(stdin))) switch (state) {
  303.         case '\n':
  304.         if ('.' == c)
  305.             state = '.';
  306.         else {
  307.             if ('\n' != c) state = '\0';
  308.             putc(c, tempfp);
  309.         }
  310.         break;
  311.         case '.':
  312.         if ('\n' == c) goto done;
  313.         state = '\0';
  314.         putc('.', tempfp);
  315.         putc(c, tempfp);
  316.         break;
  317.         default:
  318.         state = ('\n' == c) ? '\n' : '\0';
  319.         putc(c, tempfp);
  320.     }
  321. done:
  322.   if (ferror(tempfp) || fclose(tempfp)) {
  323.     fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
  324.     return NULL;
  325.   }
  326.   tempfp = freopen(tempn